home *** CD-ROM | disk | FTP | other *** search
- #include <string.h>
- #include <stdlib.h>
- #include <math.h>
-
- #include <OSUtils.h>
- #include <QuickDraw.h>
-
- #define PI (3.141592653589793)
-
- extern double sin(),cos(),fabs(),atan(),atan2(),sqrt(),exp(),log();
-
- void
- circle_through_three_points(double *x0,double *y0, double *r,
- double *x,double *y);
- void
- poly_to_arc_list(double *x0,double *y0,double *r,
- double *start_theta,double *delta_theta,
- double *x,double *y,int n,double scale,
- int flip_y);
- void
- draw_arc_list(int x,int y,double *x0,double *y0,double *r,
- double *start_theta,double *delta_theta,
- int n,double scale,double max_extra_angle);
-
- //
- // problems:
- // note heads look lousy with r>1 (I've set r=1 for now)
- //
-
- int
- draw_clef(
- char *verb, // "draw"
- char *noun, // "g_clef", "f_clef"
- int x, // horiz center of clef sign
- int y, // center line of staff
- double h, // dist between lines of staff
- void *output_info,
- void *style_info)
- {
-
- // note that the y values are positive going _up_ on the screen:
- #define N_G_CLEF_POINTS 21
- static double g_clef_x[N_G_CLEF_POINTS] = {
- 1, -5, 1, 6, 0, -9,-11, -5, 2, 4, 4,
- 2,-1,-3,-4,-2, 1, 0, -2, -4, -6
- };
- static double g_clef_y[N_G_CLEF_POINTS] = {
- -15,-10, -2,-10,-20,-17, -5, 5,15,22,24,
- 29,28,23,20, 0,-20,-25,-29, -29,-25
- };
- #define N_F_CLEF_POINTS 10
- static double f_clef_x[N_F_CLEF_POINTS] = {
- -10, -1, 3, 2,-1,-5, -7, -9,-8,-5
- };
- static double f_clef_y[N_F_CLEF_POINTS] = {
- -12, -8, 0,18,19,18, 15, 13,11,10
- };
-
- static double g_clef_x0[N_G_CLEF_POINTS],
- g_clef_y0[N_G_CLEF_POINTS],
- g_clef_r[N_G_CLEF_POINTS],
- g_clef_start_theta[N_G_CLEF_POINTS],
- g_clef_delta_theta[N_G_CLEF_POINTS];
- static double f_clef_x0[N_F_CLEF_POINTS],
- f_clef_y0[N_F_CLEF_POINTS],
- f_clef_r[N_F_CLEF_POINTS],
- f_clef_start_theta[N_F_CLEF_POINTS],
- f_clef_delta_theta[N_F_CLEF_POINTS];
- static int calculated_clefs = 0;
-
- if (!calculated_clefs) {
- poly_to_arc_list(g_clef_x0,g_clef_y0,g_clef_r,
- g_clef_start_theta,g_clef_delta_theta,
- g_clef_x,g_clef_y,N_G_CLEF_POINTS,10.*h,1);
- poly_to_arc_list(f_clef_x0,f_clef_y0,f_clef_r,
- f_clef_start_theta,f_clef_delta_theta,
- f_clef_x,f_clef_y,N_F_CLEF_POINTS,10.*h,1);
- calculated_clefs = 1;
- }
-
- if (strcmp(verb,"draw")==0) {
- if (strcmp(noun,"g_clef")==0)
- draw_arc_list(x,y,g_clef_x0,g_clef_y0,g_clef_r,
- g_clef_start_theta,g_clef_delta_theta,
- N_G_CLEF_POINTS,.01,20.);
- if (strcmp(noun,"f_clef")==0) {
- Rect rr;
- short xx,yy,radius;
- draw_arc_list(x,y,f_clef_x0,f_clef_y0,f_clef_r,
- f_clef_start_theta,f_clef_delta_theta,
- N_F_CLEF_POINTS,.01,20.);
- radius = .15*h;
- if (radius<1) radius=1;
- xx = x+.8*h;
- yy = y-1.5*h;
- SetRect(&rr,xx-radius,yy-radius,xx+radius,yy+radius);
- FillOval(&rr,&black);
- yy = y-0.5*h;
- SetRect(&rr,xx-radius,yy-radius,xx+radius,yy+radius);
- FillOval(&rr,&black);
- }
- }
- return 0;
- }
-
- // Typically, max_extra_angle should be 20. This
- // controls how much extra it draws on the beginning
- // and end of each arc, to try to make sure they connect
- // up properly. Kind of a kludge! Should improve this!
- void
- draw_arc_list(int x,int y,double *x0,double *y0,double *r,
- double *start_theta,double *delta_theta,
- int n,double scale,double max_extra_angle)
- {
- int i,xx1,yy1,xx2,yy2;
- short theta,d,zz;
- Rect rr;
- for (i=0; i<=n-3; i++) {
- xx1 = x0[i]+r[i]*cos(PI/180.*start_theta[i]);
- yy1 = y0[i]+r[i]*sin(PI/180.*start_theta[i]);
- xx2 = x0[i]+r[i]*cos(PI/180.*(start_theta[i]+delta_theta[i]));
- yy2 = y0[i]+r[i]*sin(PI/180.*(start_theta[i]+delta_theta[i]));
- if (i>0)
- LineTo((short) xx1,(short) yy1);
- // make sure we connect with previous arc
- if (r[i]*scale<1000.) {
- rr.left = x+scale*(x0[i]-r[i]);
- rr.right = x+scale*(x0[i]+r[i]);
- rr.top = y+scale*(y0[i]-r[i]);
- rr.bottom = y+scale*(y0[i]+r[i]);
-
- // make sure it doesn't leave gaps:
- theta = start_theta[i];
- zz = 10.*max_extra_angle/r[i];
- if (zz>max_extra_angle) zz=max_extra_angle;
- if (delta_theta[i]>0) {
- theta -= zz;
- d = delta_theta[i]+2*zz;
- }
- else {
- theta += zz;
- d = delta_theta[i]-2*zz;
- }
-
- FrameArc(&rr,theta,d);
- }
- else {
- MoveTo((short) xx1,(short) yy1);
- LineTo((short) xx2,(short) yy2);
- }
- MoveTo((short) xx2,(short) yy2);
- // prepare to connect up with beginning of next arc
- }
- }
-
- // This routine takes a list of points and figures out
- // how to connect them smoothly with arcs. The arc
- // connecting points i and i+1 is chosen to be an arc
- // of the circle passing through points i, i+1 and i+2.
- // (The final arc is based on the last 3 points.)
- // It's not as smart as it could be about choosing
- // the direction of the arc. To be safe, never try
- // to make an arc of >=180 degrees.
- // Everything is scaled up by a factor of scale.
- // Should scale up quite a bit to avoid rounding errors
- // in PtToAngle, but not so much that you'll overflow
- // the short ints involved.
- void
- poly_to_arc_list(double *x0,double *y0,double *r,
- double *start_theta,double *delta_theta,
- double *x,double *y,int n,double scale,
- int flip_y)
- {
- int i,j;
- Rect rr;
- Point p;
- short theta[3];
- double s;
- if (flip_y) s= -1.; else s=1;
- for (i=0; i<=n-3; i++) {
- double xx[3],yy[3];
- // For maximum precision, scale _up_ by a factor of
- // 10 now, then scale down later. Invert y's at this
- // point.
- for (j=0; j<=2; j++) {
- xx[j] = scale*x[i+j];
- yy[j] = s*scale*y[i+j];
- }
- circle_through_three_points(x0+i,y0+i,r+i,xx,yy);
-
- rr.left = x0[i]-r[i];
- rr.right = x0[i]+r[i];
- rr.top = y0[i]-r[i];
- rr.bottom = y0[i]+r[i];
-
- for (j=0; j<=2; j++) {
- #if 1
- theta[j] = 90.-180./PI*atan2(-yy[j]+y0[i],xx[j]-x0[i]);
- #else
- p.h = xx[j];
- p.v = yy[j];
- PtToAngle(&rr,p,theta+j);
- #endif
- }
- start_theta[i] = theta[0];
- if (i<n-3)
- delta_theta[i] = theta[1]-theta[0];
- else
- delta_theta[i] = theta[2]-theta[0];
- while (delta_theta[i]>180.)
- delta_theta[i] -= 360.;
- while (delta_theta[i]<-180.)
- delta_theta[i] += 360.;
- }
-
- }
-
- // Style defined by:
- // h = distance between lines of staff = height of note heads
- // r = longest axis of note divided by h (>=1)
- // phi_deg = angle of tilt in degrees
- // The only style item that must be explicitly set at the beginning
- // is h; the rest have defaults.
- //
- // Although I currently draw note-heads as two semicircles connected
- // by straight sides, there is nothing in the interface that forces
- // the calling routine to know this or that would make it difficult
- // to change the shape in the future.
- //
- // Returns 0 normally, nonzero on error.
- // Error codes:
- // -1 never specified h
- // -2 unrecognized verb
- int
- draw_note_head(
- char *verb, // "draw","left_tangent","right_tangent", or
- // "set_r","set_h","set_phi_deg"
- int x,
- int y, // center of note in current coords
- int if_filled, // filled, like 1/4 note, or open, like 1/2?
- void *output_info, // Point * for tangents
- void *style_info // pointer to double for set_r, set_h, or set_phi_deg
- )
- {
- static double r = 1.;
- static double h = -1.; // no default for h
- static double phi_deg = 20.;
-
- static int recompute_style_stuff = 1;
- static double phi, // tilt angle, converted to radians
- l, // length of long axis
- a, // radius of circular ends
- q, // 2qa = length of straight sides
- sin_phi,cos_phi,a_sin_phi,a_cos_phi,q_a_cos_phi,q_a_sin_phi;
-
- double xr,yr,xl,yl;
- int verb_was_to_set_style;
-
-
- verb_was_to_set_style = 0;
-
- if (strcmp(verb,"set_r")==0) {
- r = * (double *) style_info;
- recompute_style_stuff = 1;
- verb_was_to_set_style = 1;
- }
- if (strcmp(verb,"set_h")==0) {
- h = * (double *) style_info;
- recompute_style_stuff = 1;
- verb_was_to_set_style = 1;
- }
- if (strcmp(verb,"set_phi_deg")==0) {
- phi_deg = * (double *) style_info;
- recompute_style_stuff = 1;
- verb_was_to_set_style = 1;
- }
-
- if (h<0.) return -1;
-
- if (recompute_style_stuff) {
- recompute_style_stuff = 0;
- phi = phi_deg*PI/180.;
- sin_phi = sin(phi);
- cos_phi = cos(phi);
- l = r*h;
- q = (r-1.) / (1.-r*fabs(sin_phi));
- a = .5*h/(1.+q*fabs(sin_phi));
- a_sin_phi = a*sin_phi;
- a_cos_phi = a*cos_phi;
- q_a_cos_phi = q*a_cos_phi;
- q_a_sin_phi = q*a_sin_phi;
-
- }
-
- if (verb_was_to_set_style) return 0;
-
- // center of right-hand semicircle:
- xr = x + q_a_cos_phi;
- yr = y - q_a_sin_phi;
-
- // center of left-hand semicircle:
- xl = x - q_a_cos_phi;
- yl = y + q_a_sin_phi;
-
- if (strcmp(verb,"right_tangent")==0) {
- if (output_info != (void *) 0)
- SetPt((Point *) output_info,(short) (xr+a+.5),(short) (yr+.5));
- return 0;
- }
- if (strcmp(verb,"left_tangent")==0) {
- if (output_info != (void *) 0)
- SetPt((Point *) output_info,(short) (xl-a+.5),(short) (yl+.5));
- return 0;
- }
-
- #if 1
- if (strcmp(verb,"draw")==0) {
- Rect r;
- RgnHandle region1,region2,region3,whole_region;
- PolyHandle p;
-
- region1 = NewRgn();
- OpenRgn();
- SetRect(&r,(short) (xr-a+.5),(short) (yr-a+.5),
- (short) (xr+a+.5),(short) (yr+a+.5));
- FrameOval(&r);
- CloseRgn(region1);
-
- region2 = NewRgn();
- OpenRgn();
- p = OpenPoly();
- #define ZOT 0.5
- //-- flat top part:
- MoveTo((short) (x-a_sin_phi+q_a_cos_phi+ZOT),
- (short) (y-a_cos_phi-q_a_sin_phi+ZOT));
- LineTo((short) (x-a_sin_phi-q_a_cos_phi+ZOT),
- (short) (y-a_cos_phi+q_a_sin_phi+ZOT));
- //-- flat bottom part:
- LineTo((short) (x+a_sin_phi-q_a_cos_phi+ZOT),
- (short) (y+a_cos_phi+q_a_sin_phi+ZOT));
- LineTo((short) (x+a_sin_phi+q_a_cos_phi+ZOT),
- (short) (y+a_cos_phi-q_a_sin_phi+ZOT));
- //-- back to start:
- LineTo((short) (x-a_sin_phi+q_a_cos_phi+ZOT),
- (short) (y-a_cos_phi-q_a_sin_phi+ZOT));
- ClosePoly();
- FramePoly(p);
- CloseRgn(region2);
-
- region3 = NewRgn();
- OpenRgn();
- SetRect(&r,(short) (xl-a+.5),(short) (yl-a+.5),
- (short) (xl+a+.5),(short) (yl+a+.5));
- FrameOval(&r);
- CloseRgn(region3);
-
- whole_region = NewRgn();
- UnionRgn(region1,region2,whole_region);
- UnionRgn(whole_region,region3,whole_region);
-
- //--- finally: draw it!
- if (if_filled)
- FillRgn(whole_region,&black);
- else
- FrameRgn(whole_region);
-
- DisposeRgn(region1);
- DisposeRgn(region2);
- DisposeRgn(region3);
- DisposeRgn(whole_region);
- KillPoly(p); //deallocate memory
-
- return 0;
- }
- #endif
- #if 0
- if (strcmp(verb,"draw")==0) {
- Rect r;
- SetRect(&r,(short) (xr-a+.5),(short) (yr-a+.5),
- (short) (xr+a+.5),(short) (yr+a+.5));
- if (if_filled)
- FillArc(&r,(short) (180-phi_deg+.5),(short) -180,&black);
- else
- FrameArc(&r,(short) (180-phi_deg+.5),(short) -180);
- if (if_filled) {
- PolyHandle p;
- p = OpenPoly();
- MoveTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
- (short) (y-a_cos_phi-q_a_sin_phi+.5));
- LineTo((short) (x-a_sin_phi-q_a_cos_phi+.5),
- (short) (y-a_cos_phi+q_a_sin_phi+.5));
- LineTo((short) (x+a_sin_phi-q_a_cos_phi+.5),
- (short) (y+a_cos_phi+q_a_sin_phi+.5));
- LineTo((short) (x+a_sin_phi+q_a_cos_phi+.5),
- (short) (y+a_cos_phi-q_a_sin_phi+.5));
- LineTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
- (short) (y-a_cos_phi-q_a_sin_phi+.5));
- ClosePoly();
- FillPoly(p,&black);
- KillPoly(p);
- }
- else {
- MoveTo((short) (x-a_sin_phi+q_a_cos_phi+.5),
- (short) (y-a_cos_phi-q_a_sin_phi+.5));
- LineTo((short) (x-a_sin_phi-q_a_cos_phi+.5),
- (short) (y-a_cos_phi+q_a_sin_phi+.5));
- MoveTo((short) (x+a_sin_phi-q_a_cos_phi+.5),
- (short) (y+a_cos_phi+q_a_sin_phi+.5));
- LineTo((short) (x+a_sin_phi+q_a_cos_phi+.5),
- (short) (y+a_cos_phi-q_a_sin_phi+.5));
- }
- SetRect(&r,(short) (xl-a+.5),(short) (yl-a+.5),
- (short) (xl+a+.5),(short) (yl+a+.5));
- if (if_filled)
- FillArc(&r,(short) (360-phi_deg+.5),(short) -180,&black);
- else
- FrameArc(&r,(short) (360-phi_deg+.5),(short) -180);
- return 0;
- }
- #endif
-
- return -2;
-
- }
-
- void
- draw_staff(Rect *rec)
- {
- short t,b,l,r,y;
- double h;
- int i;
- t = rec->top;
- b = rec->bottom;
- l = rec->left;
- r = rec->right;
- h = b-t;
- h = h/4.;
- for (i=0; i<=4; i++) {
- y = t+h*i+.5;
- MoveTo(l,y);
- LineTo(r,y);
- }
- }
-
- void
- circle_through_three_points(double *x0,double *y0, double *r,
- double *x,double *y)
- {
- double xm1,ym1,xm2,ym2,a1,a2,b1,b2,lx,ly,typical_scale,z;
-
- // If either line seg is horizontal, rotate everything
- // by 20 degrees and do recursion:
- if (fabs(y[0]-y[1])<1e-6 || fabs(y[1]-y[2])<1e-6) {
- double xx[3],yy[3];
- int i;
- for (i=0; i<=2; i++) {
- xx[i] = x[i]*0.939693 - y[i]*0.342020;
- yy[i] = x[i]*0.342020 + y[i]*0.939693;
- }
- circle_through_three_points(x0,y0,r,xx,yy);
- *x0 = (*x0)*0.939693 + (*y0)*0.342020;
- *y0 = -(*x0)*0.342020 + (*y0)*0.939693;
- return;
- }
-
- // find midpoints of two line segs:
- xm1 = .5*(x[0]+x[1]);
- ym1 = .5*(y[0]+y[1]);
- xm2 = .5*(x[1]+x[2]);
- ym2 = .5*(y[1]+y[2]);
-
- typical_scale = fabs(xm1-xm2)+fabs(ym1-ym2);
-
- // find slopes of normals to line segs; guaranteed
- // not to have any problems with overflows, because
- // we already made sure nothing was exactly horizontal
- a1 = -(x[1]-x[0])/(y[1]-y[0]);
- a2 = -(x[2]-x[1])/(y[2]-y[1]);
-
- // put normals in the form y=ax+b
- b1 = ym1-a1*xm1;
- b2 = ym2-a2*xm2;
-
- // Solve eqns simultaneously to get intersection.
- // If slopes are the same, put the intersection way
- // off to one side.
- if (fabs(a1-a2)==0.) {
- // First try:
- *x0 = .5*(xm1+xm2)+1e6*typical_scale;
- *y0 = a1*(*x0)+b1;
- // Second try: now make sure it about as far off as we
- // wanted it:
- z = fabs(*x0-.5*(xm1+xm2))+fabs(*y0-.5*(ym1+ym2));
- *x0 = .5*(xm1+xm2)
- +(*(x0)-.5*(xm1+xm2))*1e6*typical_scale/z;
- *y0 = a1*(*x0)+b1;
- }
- else {
- *x0 = (b1-b2)/(a2-a1);
- *y0 = a1*(*x0)+b1;
- }
-
- lx = x[0] - *x0;
- ly = y[0] - *y0;
- *r = sqrt(lx*lx+ly*ly);
- }
-
-